package xyz.labmem.lipx.core.client.core

import io.netty.channel.ChannelInitializer
import io.netty.channel.socket.SocketChannel
import io.netty.handler.codec.LengthFieldBasedFrameDecoder
import io.netty.handler.timeout.IdleStateHandler
import xyz.labmem.common.property.ContextProperty.forwardNumProperty
import xyz.labmem.common.property.ContextProperty.reverseNumProperty
import xyz.labmem.lipx.core.Status
import xyz.labmem.lipx.core.Type.FORWARD
import xyz.labmem.lipx.core.client.core.handler.LabNettyClientHandler
import xyz.labmem.lipx.core.codec.LabMessageDecoder
import xyz.labmem.lipx.core.codec.LabMessageEncoder
import xyz.labmem.lipx.core.context.AppContext
import xyz.labmem.lipx.core.context.AppContext.Client.connectingList
import xyz.labmem.lipx.core.context.AppContext.Client.parallelPool
import xyz.labmem.lipx.core.context.AppContext.Client.rowPool
import xyz.labmem.lipx.core.entity.ClientConfig
import xyz.labmem.lipx.core.net.TcpConnection
import xyz.labmem.lipx.core.tool.LogInfo
import xyz.labmem.util.viewRun

/**
 * @description: main
 * @author: ltty
 * @date: 2022/5/27 13:11
 */
class LabSSHPenetrationClient(
    private var config: ClientConfig,
    private val renderingList: (() -> Unit)? = null
) {
    private val tcp: TcpConnection = TcpConnection()

    private var remake = false

    private var remaking = false

    private var remakeCount = 0

    init {
        release(Status.CONNECTING)
    }

    fun connect(call: (() -> Unit)? = null): LabSSHPenetrationClient {
        parallelPool.execute {
            try {
                connectingList[config.id!!] = this@LabSSHPenetrationClient
                val future = tcp.connect(
                    config.serverHost!!,
                    config.serverPort!!,
                    object : ChannelInitializer<SocketChannel>() {
                        @Throws(Exception::class)
                        override fun initChannel(ch: SocketChannel) {
                            ch.pipeline().addLast(
                                LengthFieldBasedFrameDecoder(Int.MAX_VALUE, 0, 4, 0, 4),
                                LabMessageDecoder(), LabMessageEncoder(),
                                IdleStateHandler(60, 30, 0),
                                LabNettyClientHandler(config, config.type == FORWARD) { b, r, rr, msg ->
                                    if (!remake)
                                        remake = b
                                    if (r) {
                                        remake = false
                                    }
                                    if (b) {
                                        remakeCount = 0
                                        release(Status.CONNECTED)
                                        AppContext.Client.connectList[config.id!!] = this@LabSSHPenetrationClient
                                        connectingList.remove(config.id!!)
                                        remaking = false
                                    } else {
                                        close()
                                        LogInfo.appendLogError(msg, config)
                                        release(Status.FAILED)
                                    }
                                    call?.let { it() }
                                }
                            )
                        }
                    })
                // 监听重试
                future.addListener { if (remake && !remaking) close() }
            } catch (e: Exception) {
                tcp.channel?.close()
                release(Status.FAILED)
                LogInfo.appendLogError("lipx连接错误:${e.message}", config)
                call?.let { it() }
                System.gc()
            }
        }
        return this
    }


    fun close() {
        LogInfo.appendLog("正在断开当前连接。。", config)
        try {
            remake = false
            tcp.channel?.close()
            LogInfo.appendLog("连接已断开", config)
        } catch (e: Exception) {
            LogInfo.appendLogError("连接断开错误:${e.message}", config)
        } finally {
            rowPool.execute {
                AppContext.Client.allList[config.id]?.status = Status.IDLE
                AppContext.Client.connectList.remove(config.id)
                connectingList.remove(config.id)
                viewRun { renderingList?.let { it() } }
                if (config.type == FORWARD)
                    forwardNumProperty.value(forwardNumProperty.value().toInt() - 1)
                else
                    reverseNumProperty.value(reverseNumProperty.value().toInt() - 1)
                System.gc()
            }
        }
    }

    private fun release(status: Status) {
        rowPool.execute {
            if (AppContext.Client.allList[config.id]?.status != Status.CONNECTED && status == Status.CONNECTED) {
                if (config.type == FORWARD)
                    forwardNumProperty.value(forwardNumProperty.value().toInt() + 1)
                else
                    reverseNumProperty.value(reverseNumProperty.value().toInt() + 1)
            } else if (AppContext.Client.allList[config.id]?.status == Status.CONNECTED && (status == Status.FAILED || status == Status.IDLE)) {
                if (config.type == FORWARD)
                    forwardNumProperty.value(forwardNumProperty.value().toInt() - 1)
                else
                    reverseNumProperty.value(reverseNumProperty.value().toInt() - 1)
            }
            AppContext.Client.allList[config.id]?.status = status
            viewRun { renderingList?.let { it() } }
        }
    }

    fun restart(newConfig: ClientConfig) {
        config = newConfig
        release(Status.RE_CONNECT)
        LogInfo.appendLog("正在断开当前连接。。", config)
        try {
            remake = false
            tcp.channel?.close()
            LogInfo.appendLog("连接已断开，准备重启", config)
            connect()
        } catch (e: Exception) {
            release(Status.IDLE)
            LogInfo.appendLogError("连接重启错误:${e.message}", config)
        }
    }

}